package emongo

import (
    "time"

    "github.com/globalsign/mgo"
)

type EMongoConfig struct {
    MongoUrl string `ini:"mongourl"`
    Timeout  int    `ini:"timeout"`
}

type EMongo struct {
    Config *EMongoConfig
    Session *mgo.Session
}

func NewEMongo(conf *EMongoConfig) (*EMongo, error) {
    dialInfo, err := mgo.ParseURL(conf.MongoUrl)
    if err != nil {
        return nil, err
    }

    dialInfo.Timeout = time.Duration(conf.Timeout) * time.Millisecond
    s, err := mgo.DialWithInfo(dialInfo)
    if err != nil {
        return nil, err
    }

    return &EMongo{Config: conf, Session: s}, nil
}

func (e *EMongo) Connect(db, collection string) (*mgo.Session, *mgo.Collection) {
    s := e.Session.Copy()
    c := s.DB(db).C(collection)
    return s, c
}

func (e *EMongo) connect(db, collection string) (*mgo.Session, *mgo.Collection) {
    s := e.Session.Copy()
    c := s.DB(db).C(collection)
    return s, c
}

func (e *EMongo) Insert(db, collection string, docs ...interface{}) error {
    s, c := e.connect(db, collection)
    defer s.Close()
    err := c.Insert(docs...)
    if mgo.IsDup(err) {
        return nil
    }

    return err
}

func (e *EMongo) Upsert(db, collection string, query, docs interface{}) error {
    change := mgo.Change{
        Update: docs,
        Upsert: true,
        ReturnNew: true,
    }
    s, c := e.connect(db, collection)
    defer s.Close()
    _, err := c.Find(query).Apply(change, docs)
    if mgo.IsDup(err) {
        return nil
    }

    return err
}

func (e *EMongo) IsExist(db, collection string, query interface{}) bool {
    s, c := e.connect(db, collection)
    defer s.Close()
    count, _ := c.Find(query).Count()
    return count > 0
}

func (e *EMongo) FindOne(db, collection string, query, selector, result interface{}) error {
    s, c := e.connect(db, collection)
    defer s.Close()
    return c.Find(query).Select(selector).One(result)
}

func (e *EMongo) FindAll(db, collection string, query, selector, result interface{}) error {
    s, c := e.connect(db, collection)
    defer s.Close()
    return c.Find(query).Select(selector).All(result)
}

func (e *EMongo) FindLimit(db, collection string, query interface{}, limit int, result interface{}) error {
    s, c := e.connect(db, collection)
    defer s.Close()
    return c.Find(query).Limit(limit).All(result)
}

func (e *EMongo) Update(db, collection string, query, update interface{}) error {
    s, c := e.connect(db, collection)
    defer s.Close()
    return c.Update(query, update)
}

func (e *EMongo) Remove(db, collection string, query interface{}) error {
    s, c := e.connect(db, collection)
    defer s.Close()
    return c.Remove(query)
}
